var sm4Util = require("./sm4Util.js");
var SboxTable = new Array();
SboxTable[0] = new Array(0xd6, 0x90, 0xe9, 0xfe, 0xcc, 0xe1, 0x3d, 0xb7, 0x16, 0xb6, 0x14, 0xc2, 0x28, 0xfb, 0x2c, 0x05);
SboxTable[1] = new Array(0x2b, 0x67, 0x9a, 0x76, 0x2a, 0xbe, 0x04, 0xc3, 0xaa, 0x44, 0x13, 0x26, 0x49, 0x86, 0x06, 0x99);
SboxTable[2] = new Array(0x9c, 0x42, 0x50, 0xf4, 0x91, 0xef, 0x98, 0x7a, 0x33, 0x54, 0x0b, 0x43, 0xed, 0xcf, 0xac, 0x62);
SboxTable[3] = new Array(0xe4, 0xb3, 0x1c, 0xa9, 0xc9, 0x08, 0xe8, 0x95, 0x80, 0xdf, 0x94, 0xfa, 0x75, 0x8f, 0x3f, 0xa6);
SboxTable[4] = new Array(0x47, 0x07, 0xa7, 0xfc, 0xf3, 0x73, 0x17, 0xba, 0x83, 0x59, 0x3c, 0x19, 0xe6, 0x85, 0x4f, 0xa8);
SboxTable[5] = new Array(0x68, 0x6b, 0x81, 0xb2, 0x71, 0x64, 0xda, 0x8b, 0xf8, 0xeb, 0x0f, 0x4b, 0x70, 0x56, 0x9d, 0x35);
SboxTable[6] = new Array(0x1e, 0x24, 0x0e, 0x5e, 0x63, 0x58, 0xd1, 0xa2, 0x25, 0x22, 0x7c, 0x3b, 0x01, 0x21, 0x78, 0x87);
SboxTable[7] = new Array(0xd4, 0x00, 0x46, 0x57, 0x9f, 0xd3, 0x27, 0x52, 0x4c, 0x36, 0x02, 0xe7, 0xa0, 0xc4, 0xc8, 0x9e);
SboxTable[8] = new Array(0xea, 0xbf, 0x8a, 0xd2, 0x40, 0xc7, 0x38, 0xb5, 0xa3, 0xf7, 0xf2, 0xce, 0xf9, 0x61, 0x15, 0xa1);
SboxTable[9] = new Array(0xe0, 0xae, 0x5d, 0xa4, 0x9b, 0x34, 0x1a, 0x55, 0xad, 0x93, 0x32, 0x30, 0xf5, 0x8c, 0xb1, 0xe3);
SboxTable[10] = new Array(0x1d, 0xf6, 0xe2, 0x2e, 0x82, 0x66, 0xca, 0x60, 0xc0, 0x29, 0x23, 0xab, 0x0d, 0x53, 0x4e, 0x6f);
SboxTable[11] = new Array(0xd5, 0xdb, 0x37, 0x45, 0xde, 0xfd, 0x8e, 0x2f, 0x03, 0xff, 0x6a, 0x72, 0x6d, 0x6c, 0x5b, 0x51);
SboxTable[12] = new Array(0x8d, 0x1b, 0xaf, 0x92, 0xbb, 0xdd, 0xbc, 0x7f, 0x11, 0xd9, 0x5c, 0x41, 0x1f, 0x10, 0x5a, 0xd8);
SboxTable[13] = new Array(0x0a, 0xc1, 0x31, 0x88, 0xa5, 0xcd, 0x7b, 0xbd, 0x2d, 0x74, 0xd0, 0x12, 0xb8, 0xe5, 0xb4, 0xb0);
SboxTable[14] = new Array(0x89, 0x69, 0x97, 0x4a, 0x0c, 0x96, 0x77, 0x7e, 0x65, 0xb9, 0xf1, 0x09, 0xc5, 0x6e, 0xc6, 0x84);
SboxTable[15] = new Array(0x18, 0xf0, 0x7d, 0xec, 0x3a, 0xdc, 0x4d, 0x20, 0x79, 0xee, 0x5f, 0x3e, 0xd7, 0xcb, 0x39, 0x48);

var CK = new Array(
  0x00070e15, 0x1c232a31, 0x383f464d, 0x545b6269,
  0x70777e85, 0x8c939aa1, 0xa8afb6bd, 0xc4cbd2d9,
  0xe0e7eef5, 0xfc030a11, 0x181f262d, 0x343b4249,
  0x50575e65, 0x6c737a81, 0x888f969d, 0xa4abb2b9,
  0xc0c7ced5, 0xdce3eaf1, 0xf8ff060d, 0x141b2229,
  0x30373e45, 0x4c535a61, 0x686f767d, 0x848b9299,
  0xa0a7aeb5, 0xbcc3cad1, 0xd8dfe6ed, 0xf4fb0209,
  0x10171e25, 0x2c333a41, 0x484f565d, 0x646b7279
);

var FK = new Array(0xa3b1bac6, 0x56aa3350, 0x677d9197, 0xb27022dc);

function bigxor(a, b) {
  return a ^ b
}


function rotl(a, n) {
  return (a << n) | (a >>> (32 - n))
}

function prefixInteger(str, length) {
  return Array(length + 1).join("0").split("").concat(String(str).split(""))
    .slice(-length).join("");
}

function sm4Sbox(a) {
  var b1 = SboxTable[(a & 0xf0000000) >>> 28][(a & 0x0f000000) >>> 24]
  var b2 = SboxTable[(a & 0x00f00000) >>> 20][(a & 0x000f0000) >>> 16]
  var b3 = SboxTable[(a & 0x0000f000) >>> 12][(a & 0x00000f00) >>> 8]
  var b4 = SboxTable[(a & 0x000000f0) >>> 4][(a & 0x0000000f) >>> 0]
  return (b1 << 24) | (b2 << 16) | (b3 << 8) | (b4 << 0)
}


function L1(B) {
  return B ^ rotl(B, 2) ^ rotl(B, 10) ^ rotl(B, 18) ^ rotl(B, 24);
}

function L2(B) {
  return B ^ rotl(B, 13) ^ rotl(B, 23);
  //	return B^(B<<13|B>>>19)^(B<<23|B>>>9);
}


function GET_ULONG_BE(a) {
  a = sm4Sbox(a)
  return bigxor(bigxor(bigxor(a, leftshift(a, 2)), bigxor(leftshift(a, 10), leftshift(a, 18))), leftshift(a, 24))
}

function PUT_ULONG_BE(b) {
  b = sm4Sbox(b)
  return bigxor(b, bigxor(leftshift(b, 13), leftshift(b, 23)));
}

function xorkey(key) {
  var result = new Array();
  for (var i = 0; i < key.length; i += 8) {
    result.push(parseInt(key.slice(i, i + 8), 16))
  }
  return result;
}

function sm4_getkey(MK) {
  var K = new Array();
  var rk = new Array();
  K[0] = bigxor(MK[0], FK[0]);
  K[1] = bigxor(MK[1], FK[1]);
  K[2] = bigxor(MK[2], FK[2]);
  K[3] = bigxor(MK[3], FK[3]);

  var mid;
  for (var i = 0; i < 32; i += 4) {
    //K[i+4] = bigxor(K[i], PUT_ULONG_BE(bigxor(bigxor(K[i+1], K[i+2]), bigxor(K[i+3], CK[i]))));

    mid = K[1] ^ K[2] ^ K[3] ^ CK[i + 0];
    mid = sm4Sbox(mid);
    K[0] = K[0] ^ L2(mid);
    rk[i + 0] = K[0];

    mid = K[2] ^ K[3] ^ K[0] ^ CK[i + 1]
    mid = sm4Sbox(mid);
    K[1] = K[1] ^ L2(mid);
    rk[i + 1] = K[1];


    mid = K[3] ^ K[0] ^ K[1] ^ CK[i + 2]
    mid = sm4Sbox(mid);
    K[2] = rk[i + 2] = K[2] ^ L2(mid);
    rk[i + 2] = K[2];

    mid = K[0] ^ K[1] ^ K[2] ^ CK[i + 3]
    mid = sm4Sbox(mid);
    K[3] = K[3] ^ L2(mid);
    rk[i + 3] = K[3];
  };



  return rk;
}

function KJUR_encrypt_sm4(messsage, key, method) {
  var MK = key;
  var x = messsage;
  var rk = sm4_getkey(MK);


  for (var i = 0; i < 32; i += 4) {
    //console.log("i=" + i + " rk[i]=" + rk[i]);
    var mid;
    mid = x[1] ^ x[2] ^ x[3] ^ rk[i + 0];
    mid = sm4Sbox(mid);
    x[0] = x[0] ^ L1(mid);   //x4

    mid = x[2] ^ x[3] ^ x[0] ^ rk[i + 1];
    mid = sm4Sbox(mid);
    x[1] = x[1] ^ L1(mid);	//x5

    mid = x[3] ^ x[0] ^ x[1] ^ rk[i + 2];
    mid = sm4Sbox(mid);
    x[2] = x[2] ^ L1(mid);	//x6

    mid = x[0] ^ x[1] ^ x[2] ^ rk[i + 3];
    mid = sm4Sbox(mid);
    x[3] = x[3] ^ L1(mid);	//x7
  };
  var Y = new Array(x[3], x[2], x[1], x[0])
  return Y;
}

function PKCS7_padding_encode2(data) {
  var result = new Array();
  for (var i = 0; i < data.length; i++) {
    result.push(data.charCodeAt(i));
  };
  var size = 16 - result.length % 16
  for (i = 0; i < size; i++) {
    result.push(size)
  };
  return result;
}


function PKCS7_padding_encode2_hex(hex) {
  var result = new Array();
  for (var i = 0; i < hex.length; i += 2) {
    result.push(parseInt(hex.substr(i, 2), 16));
  };
  var size = 16 - result.length % 16
  for (i = 0; i < size; i++) {
    result.push(size)
  };
  return result;
}


function sm4_encode_cbc2_hex(hex, key) {

  var iv = new Array(0x0F0E0D0C, 0x0B0A0908, 0x07060504, 0x03020100);
  var result = new Array();
  key = xorkey(key);
  var data = PKCS7_padding_encode2_hex(hex);

  for (var x = 0; x < data.length / 16; x += 1) {
    var message = new Array();

    message[0] = (data[x * 16 + 0] << 24) | (data[x * 16 + 1] << 16) | (data[x * 16 + 2] << 8) | (data[x * 16 + 3] << 0);
    message[1] = (data[x * 16 + 4] << 24) | (data[x * 16 + 5] << 16) | (data[x * 16 + 6] << 8) | (data[x * 16 + 7] << 0);
    message[2] = (data[x * 16 + 8] << 24) | (data[x * 16 + 9] << 16) | (data[x * 16 + 10] << 8) | (data[x * 16 + 11] << 0);
    message[3] = (data[x * 16 + 12] << 24) | (data[x * 16 + 13] << 16) | (data[x * 16 + 14] << 8) | (data[x * 16 + 15] << 0);


    var msg1 = bjca_mssp_uint32ToStr(message);

    var tbc = new Array();
    for (var i = 0; i < iv.length; i++) {
      tbc.push(bigxor(iv[i], message[i]));
    };
    ciphertext = KJUR_encrypt_sm4(tbc, key, "cbc")
    iv = ciphertext
    result = result.concat(ciphertext);

    //console.log(  bjca_mssp_uint32ToStr(ciphertext));


  }
  //	alert(result);

  var ret = new Array();

  for (var i = 0; i < result.length; i++) {
    var ri = result[i];
    ret.push(((ri >>> 24) & 0xff).toString(16));
    ret.push(((ri >>> 16) & 0xff).toString(16));
    ret.push(((ri >>> 8) & 0xff).toString(16));
    ret.push((ri & 0xff).toString(16));
  }
  //return ret;
  //	alert(ret);
  return sm4Util.bjca_mssp_charArray2Str_sm4(ret);
}


function sm4_encode_cbc2(data, key) {

  var iv = new Array(0x0F0E0D0C, 0x0B0A0908, 0x07060504, 0x03020100);
  var result = new Array();
  key = xorkey(key);
  data = PKCS7_padding_encode2(data);

  for (var x = 0; x < data.length / 16; x += 1) {
    var message = new Array();
    message[0] = (data[x * 16 + 0] << 24) | (data[x * 16 + 1] << 16) | (data[x * 16 + 2] << 8) | (data[x * 16 + 3] << 0)
    message[1] = (data[x * 16 + 4] << 24) | (data[x * 16 + 5] << 16) | (data[x * 16 + 6] << 8) | (data[x * 16 + 7] << 0)
    message[2] = (data[x * 16 + 8] << 24) | (data[x * 16 + 9] << 16) | (data[x * 16 + 10] << 8) | (data[x * 16 + 11] << 0)
    message[3] = (data[x * 16 + 12] << 24) | (data[x * 16 + 13] << 16) | (data[x * 16 + 14] << 8) | (data[x * 16 + 15] << 0)

    var tbc = new Array();
    for (var i = 0; i < iv.length; i++) {
      tbc.push(bigxor(iv[i], message[i]));
    };
    var ciphertext = KJUR_encrypt_sm4(tbc, key, "cbc")
    iv = ciphertext
    result = result.concat(ciphertext);

  }
  //	alert(result);

  var ret = new Array();

  for (var i = 0; i < result.length; i++) {
    var ri = parseInt(result[i], 16);
    ret.push(((ri >>> 24) & 0xff).toString(16));
    ret.push(((ri >>> 16) & 0xff).toString(16));
    ret.push(((ri >>> 8) & 0xff).toString(16));
    ret.push((ri & 0xff).toString(16));
  }
  //return ret;
  //	alert(ret);
  return sm4Util.bjca_mssp_charArray2Str_sm4(ret);
}


module.exports = {
  sm4_encode_cbc2
}

